const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function MyPromise(callback) {
  const self = this;
  self.status = PENDING;
  self.value = null;
  self.reason = null;
  self.onFulfilledCb = [];
  self.onRejectedCb = [];

  function resolve(value) {
    if (self.status === PENDING) {
      self.status = FULFILLED;
      self.value = value;
      setTimeout(() => {
        self.onFulfilledCb.forEach((cb) => cb(value));
      }, 0);
    }
  }
  function reject(reason) {
    if (self.status === PENDING) {
      self.status = REJECTED;
      self.reason = reason;
      setTimeout(() => {
        self.onRejectedCb.forEach((cb) => cb(reason));
      }, 0);
    }
  }

  try {
    callback(resolve, reject);
  } catch (err) {
    reject(err);
  }
}

MyPromise.prototype.then = function (onFulfilledCb, onRejectedCb) {
  const that = this;
  return new MyPromise((resolve, reject) => {
    function handleResolution(value) {
      try {
        const res = onFulfilledCb ? onFulfilledCb(value) : value;
        if (res instanceof MyPromise) {
          res.then(resolve, reject);
        } else {
          resolve(res);
        }
      } catch (err) {
        reject(err);
      }
    }

    function handleRejection(reason) {
      try {
        const res = onRejectedCb ? onRejectedCb(reason) : reason;
        if (res instanceof MyPromise) {
          res.then(resolve, reject);
        } else {
          reject(res);
        }
      } catch (err) {
        reject(err);
      }
    }

    if (that.status === FULFILLED) {
      handleResolution(that.value);
    } else if (that.status === REJECTED) {
      handleRejection(that.reason);
    } else {
      that.onFulfilledCb.push(handleResolution);
      that.onRejectedCb.push(handleRejection);
    }
  });
};
MyPromise.prototype.catch = function (onRejectedCb) {
  return this.then(null, onRejectedCb);
};
MyPromise.resolve = function (value) {
  return new MyPromise((resolve) => resolve(value));
};

MyPromise.prototype.finally = function (cb) {
  return this.then(
    (value) => {
      return MyPromise.resolve(cb()).then(() => value);
    },
    (reason) => {
      return MyPromise.resolve(cb()).then(() => {
        throw reason;
      });
    }
  );
};
const p = new MyPromise((resolve, reject) => {
  resolve(1);
  reject(2);
});
p.then((x) => {
  console.log(x);
  return x + 1;
})
  .then((x) => {
    console.log(x);
    return x + 1;
  })
  .then((x) => {
    console.log(x);
    return x;
  })
  .catch((err) => {
    console.log("catch", err);
  })
  .finally(() => {
    console.log("finally");
    return "4";
  })
  .then((x) => {
    console.log(x);
  });
